package com.superlc.ssm.core.shiro.config;


import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.superlc.ssm.config.SsmProperties;
import com.superlc.ssm.core.shiro.filter.SsmUserApiFilter;
import com.superlc.ssm.core.shiro.filter.ShiroFormAuthenticationFilter;
import com.superlc.ssm.core.shiro.filter.ShiroPermAuthorizationFilter;
import com.superlc.ssm.core.shiro.filter.ShiroRolesAuthorizationFilter;
import com.superlc.ssm.core.shiro.session.MyShiroSessionManager;
import com.superlc.ssm.core.shiro.ShiroConst;
import com.superlc.ssm.core.shiro.realm.SysShiroRealm;
import com.superlc.ssm.core.shiro.filter.AddPrincipalToSessionFilter;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.ShiroHttpSession;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.session.mgt.ServletContainerSessionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;

/**
 * @ProjectName: ssm$
 * @Package: com.superlc.ssm.config$
 * @ClassName: ShiroConfig$
 * @Description: Shiro 配置 （比较全面）
 * @Author: super
 * @CreateDate: 2018/8/16$ 12:27$
 * @UpdateUser: 更新者
 * @UpdateDate: 2018/8/16$ 12:27$
 * @UpdateRemark: 更新内容
 * @Version: 1.0
 */
@Configuration
public class ShiroConfig {

    private static Logger logger = LoggerFactory.getLogger(ShiroConfig.class);

    /**
     * spring session管理器（多机环境）
     */
    @Bean
    @ConditionalOnProperty(prefix = "ssm", name = "spring-session-open", havingValue = "true")
    public ServletContainerSessionManager servletContainerSessionManager() {
        return new ServletContainerSessionManager();
    }

    /**
     * session管理器(单机环境)
     * 使用了Shiro的Native Session Manager，替代了Tomcat本身的Session Manager
     */
    @Bean(ShiroConst.DEFAULT_SESSION_MANAGER_NAME)
    @ConditionalOnProperty(prefix = "ssm", name = "spring-session-single", havingValue = "true")
    public DefaultWebSessionManager defaultWebSessionManager(@Qualifier(ShiroConst.EHCACHE_MANAGER_NAME) CacheManager cacheShiroManager, SsmProperties ssmProperties) {
        MyShiroSessionManager sessionManager = new MyShiroSessionManager();
        sessionManager.setCacheManager(cacheShiroManager);
        sessionManager.setSessionValidationInterval(ssmProperties.getSessionValidationInterval() * 1000);
        sessionManager.setGlobalSessionTimeout(ssmProperties.getSessionInvalidateTime() * 1000);
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setSessionValidationSchedulerEnabled(true);
        Cookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
        cookie.setName("shiroCookie");
        cookie.setHttpOnly(true);
        sessionManager.setSessionIdCookie(cookie);
        return sessionManager;
    }
    /**
     * 凭证匹配器
     * （由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
     * ）密码的加密解密方式
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        //散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(2);
        //散列的次数，比如散列两次，相当于 md5(md5(""));
        return hashedCredentialsMatcher;
    }

    @Bean
    public SysShiroRealm myShiroRealm(){
        SysShiroRealm myShiroRealm = new SysShiroRealm();
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myShiroRealm;
    }

    /**
     * 缓存管理器 使用ehcache实现
     */
    @Bean(ShiroConst.EHCACHE_MANAGER_NAME)
    @ConditionalOnProperty(prefix = "ssm" ,name ="ehcache-open" ,havingValue = "true")
    public CacheManager getCacheShiroManager(EhCacheManagerFactoryBean ehCacheManagerFactoryBean){
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManager(ehCacheManagerFactoryBean.getObject());
        return ehCacheManager ;
    }


    /**
     * rememberMe管理器
     */
    @Bean
    public  CookieRememberMeManager rememberMeManager(SimpleCookie  rememberMeCookie){
        CookieRememberMeManager manager = new CookieRememberMeManager();
        manager.setCipherKey(Base64.decode("Z3VucwAAAAAAAAAAAAAAAA=="));
        manager.setCookie( rememberMeCookie);
        return manager ;
    }

    /**
     * 记住密码 cookie
     * @return
     */
    @Bean
    public SimpleCookie rememberMeCookie(){
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        simpleCookie.setHttpOnly(true);
        simpleCookie.setMaxAge(7*24*60*60);
        return simpleCookie;
    }


    /**
     * @method  securityManager
     * @description 配置SecurityManager 安全管理配置
     * @date: 2018/8/16 12:50
     * @author: super
     * @param
     * @return org.apache.shiro.mgt.SecurityManager
     */

    @Bean(ShiroConst.EHCACHE_SECURITY_MANAGER_NAME)
    @ConditionalOnProperty(prefix = "ssm" ,name ="ehcache-open" ,havingValue = "true")
    public DefaultWebSecurityManager securityManager(CookieRememberMeManager rememberMeManager,
                                                     @Qualifier(ShiroConst.EHCACHE_MANAGER_NAME) CacheManager cacheShiroManager ,@Qualifier(ShiroConst.DEFAULT_SESSION_MANAGER_NAME) SessionManager sessionManager){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(this.myShiroRealm());
        defaultWebSecurityManager.setCacheManager(cacheShiroManager);
        defaultWebSecurityManager.setSessionManager(sessionManager);
        defaultWebSecurityManager.setRememberMeManager(rememberMeManager);
        return defaultWebSecurityManager ;
    }
   /* @Bean
    public SecurityManager securityManager(CacheManager cacheManager){

        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        securityManager.setRememberMeManager(rememberMeManager(rememberMeCookie()));
        securityManager.setCacheManager(cacheManager);
        securityManager.setSessionManager();
        return securityManager;
    }*/

    @Bean
    public ShiroFilterFactoryBean shiroFilter(@Qualifier(ShiroConst.CURRENT_CACHE_SECURITY_MANAGER) SecurityManager securityManager){
        ///方法配置了@Bean 方法参数会自动@Autowired
        System.out.println("ShiroConfiguration.shiroFilter()");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/index");
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");


        ///加入自定义的过滤器
        Map<String,Filter>  shiroFilterMap = new HashMap<>();
        SsmUserApiFilter ssmUserFilter = new SsmUserApiFilter();
        AddPrincipalToSessionFilter addPrincipalToSessionFilter = new AddPrincipalToSessionFilter();
        ShiroFormAuthenticationFilter formAuthenticationFilter  = new ShiroFormAuthenticationFilter();
        ShiroRolesAuthorizationFilter rolesAuthorizationFilter = new ShiroRolesAuthorizationFilter();
        ShiroPermAuthorizationFilter permAuthorizationFilter = new ShiroPermAuthorizationFilter();
        ///添加自定义的过滤器 ，也可以覆盖同名的过滤器 如 user author
        shiroFilterMap.put("apiUser",ssmUserFilter);
        shiroFilterMap.put("addPrinciple",addPrincipalToSessionFilter);
        shiroFilterMap.put("form",formAuthenticationFilter);
        shiroFilterMap.put("perm",permAuthorizationFilter);
        shiroFilterMap.put("roles",rolesAuthorizationFilter);

        shiroFilterFactoryBean.setFilters(shiroFilterMap);

        //配置过滤器链
        Map<String,String> shiroFilterChainMap = new LinkedHashMap<String,String>();
        //静态资源可以匿名访问
        shiroFilterChainMap.put("/static/**","anon");
        //配置退出过滤器，具体的退出代码shiro已经实现了
        shiroFilterChainMap.put("/logout","logout");
        //配置登陆url可以 匿名访问
        shiroFilterChainMap.put("/login","anon");
        shiroFilterChainMap.put("/api/auth/login","anon");

        //<!-- 过滤链定义，从上向下顺序执行，一般将/**放在最为下边 -->:这是一个坑呢，一不小心代码就不好使了;
        //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
        //”user”级别的（也就是说不用登录即可访问，只须设置了rememberMe），但是如果这些url使用了Session里面的数据，就会抛出异常，因为此时这个用户对应的Session已经不存在了。
        //user  authc  logout 其实都是自带的过滤器

        shiroFilterChainMap.put("/api/**","apiUser");

        //给链接添加认证 和 角色要求
        shiroFilterChainMap.put("/admin/**","authc,roles[admin]") ;

        //给链接添加 权限要求
        shiroFilterChainMap.put("/admin/add**","perms[admin:add]");

        shiroFilterChainMap.put("/**", "addPrinciple,user");
//        shiroFilterChainMap.put("/**", "authc");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(shiroFilterChainMap);

        return shiroFilterFactoryBean;

    }
    /**
     *  开启shiro aop注解支持.
     *  使用代理方式;所以需要开启代码支持;
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier(ShiroConst.CURRENT_CACHE_SECURITY_MANAGER)SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    @Bean(name="simpleMappingExceptionResolver")
    public SimpleMappingExceptionResolver createSimpleMappingExceptionResolver() {
        SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();
        Properties mappings = new Properties();
        //数据库异常处理
        mappings.setProperty("DatabaseException", "databaseError");
        mappings.setProperty("UnauthorizedException","403");
        // None by default
        r.setExceptionMappings(mappings);
        // No default
        r.setDefaultErrorView("error");
        // Default is "exception"
        r.setExceptionAttribute("ex");
        return r;
    }

    /**
     * 配置shiro 方言标签
     * @return
     */
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }



}
